package ex1.game;

import ex1.ga.Chromosome;

/**
 * 
 * PD player which acts according to history
 *
 */
public class HistoryLookupPlayer implements Player {

	private int _historyLevel;
	private Action _myLastAction = null;
	private History _history;
	private Chromosome _historyLookup;


	
	public HistoryLookupPlayer(int historyLevel, Chromosome lookupChromosome)
	{
		this._historyLookup = lookupChromosome;
		this._historyLevel = historyLevel;
		this.initInitialHistory();
	}
	
	@Override
	public Action playGame(PrisonersDilemmaGame game, Action otherPlayerLastAction) {
		
		
		if(otherPlayerLastAction == null)
		{
			//this is the first iteration in a new game
			_myLastAction = null;
			initInitialHistory();
		}
		
		if(_myLastAction != null)
		{
			_history.UpdateHistory(_myLastAction, otherPlayerLastAction);
		}
		
		_myLastAction = Action.getAction(
				_historyLookup.getRepresentation()[this._history.hashCode()]);
		return _myLastAction;
	}

	@Override
	public String toString() 
	{
		return "HistoryLookupPlayer: Current History = " + this._history;
	}
	
	private void initInitialHistory() {
		
		boolean[] bits = _historyLookup.getRepresentation();
		
		int fromIndex = (int)Math.pow(4,_historyLevel);
		
		this._history = new History(bits, fromIndex);
	}
	
	private class History
	{
		private Action[] _representation;
		private int _hash;
		
		public History(boolean[] bits, int fromIndex)
		{
			_representation = new Action[2 * _historyLevel];
			for (int i = 0; i < _historyLevel * 2; i++) 
			{
				_representation[i] = Action.getAction(bits[fromIndex + i]);
			}
			
			this.UpdateHash();
		}
		
		//update actions in history
		public void UpdateHistory(Action myAction, Action opponentAction)
		{
			
			for (int i = 0; i < _historyLevel*2 - 2; i++)
			{
				_representation[i] = _representation[i+2];
			}
			
			if(_historyLevel > 0)
			{
				_representation[_historyLevel*2 - 2] = myAction;
				_representation[_historyLevel*2 - 1] = opponentAction;
			}
			
			this.UpdateHash();
		}
		
		@Override
		public boolean equals(Object obj) 
		{
			return (this._hash == ((History)obj)._hash);
		};
		
		@Override
		public int hashCode() {
			return this._hash;
		}
		
		@Override
		public String toString() {
			
			StringBuilder sb = new StringBuilder();
			for (Action ac : _representation) {
				sb.append(ac.toString().charAt(0));
			}
			
			sb.append(": " + hashCode());
			return sb.toString();
		}
		
		//the hash represents a specific history
		private void UpdateHash() 
		{
			_hash = 0;
			
			int len = this._representation.length;
			for (int i = 0; i < len; i++)
			{
				_hash += (Math.pow(2, len - i - 1) * (_representation[i].getBit() ? 1 : 0));
			}
			
		}
	}
	

}
